package com.ringtheweb.common.configuration;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ringtheweb.common.util.ClassUtils;

/**
 * Factory of {@link IConfiguration}.
 * <p> The loading strategy is the following:
 * <ul>
 * <li>Loading of every file named sipphony.properties in the CLASSPATH
 * <li>Loading of a external configuration file whose named can be 
 * defined with the JVM system property <b>config.dir</b>
 * <li>Loading of the JVM system properties
 * 
 * @author ldewavri
 *
 */

public class ConfigurationFactory {
	
	
	private static Log LOG = LogFactory.getLog(ConfigurationFactory.class);
	
	private static IConfiguration configuration = null;
	
	public static final String SIPPHONY_CONFIG_FILE="sipphony.properties";
	
	public static final String SIPPHONY_CONFIG_PROPERTY="config.dir";	
	
	
	private ConfigurationFactory() {
		throw new UnsupportedOperationException("Can not be instantiated");
	}
	
	public static synchronized IConfiguration getInstance() {
		
		if (null == configuration) {
				init();
		}
		return configuration;
		
	}
	
	private static synchronized void init() {
		
		URL externalConfigURL = null ;
		

			String configurationDir = System.getProperty(SIPPHONY_CONFIG_PROPERTY);
			
			if (null != configurationDir) {
				LOG.info(String.format("No external directory configured '%s'",
						configurationDir));
				File externalConfigFile = new File(configurationDir,SIPPHONY_CONFIG_FILE);
				
				if (! externalConfigFile.exists() || ! externalConfigFile.canRead()) {
					LOG.warn(String.format("Unable to read the external configuration file '%s'",
							externalConfigFile.getAbsolutePath()));
				}
				else {
					try {
						externalConfigURL = externalConfigFile.toURI().toURL();
					}
					catch(MalformedURLException e) {
						LOG.error(String.format("Unable to read external file '%s'",
								externalConfigFile.getAbsolutePath()));
					}
					
				}
				
			}
			
			List<URL> urls = getUrlsForResource(SIPPHONY_CONFIG_FILE);

			
			CompositeConfiguration config = new CompositeConfiguration();
			// Load system properties
			config.addConfiguration(new SystemConfiguration());
			
			// Load external configuration file
			try {
				if (null != externalConfigURL) {
					if (LOG.isDebugEnabled()) {
						LOG.debug(String.format("Adding external configuration file '%s'",
								externalConfigURL.toExternalForm()));
					}
					PropertiesConfiguration propsConf = new PropertiesConfiguration(externalConfigURL);
					propsConf.setReloadingStrategy(new FileChangedReloadingStrategy());
					config.addConfiguration(propsConf);

				}
			}
			catch(ConfigurationException e ){
				LOG.error(String.format("Erreur de chargement de la configuration externe '%s'",
						externalConfigURL.getFile()), e);
			}
			
			// Load internal configuration files
			for(URL internalURL : urls) {
			
				if (null != internalURL) {
					try {
						config.addConfiguration(new PropertiesConfiguration(internalURL));
						if (LOG.isDebugEnabled()) {
							LOG.debug(String.format("Added configuration file '%s'",
									internalURL.toExternalForm()));
						}
					}
					catch(ConfigurationException e) {
						LOG.error(String.format("Unable to load internal configuration file '%s'",
								internalURL.getFile()), e);
					}
				}
			}

			
			configuration = new CommonsConfigurationAdapter(config);
		}
	
	
	public static synchronized void setInstance(final IConfiguration instance) {
		configuration =  instance;
	}
	
	
	
	/**
	 * Get the URL for the resource
	 * @param resource
	 * @return l'URL 
	 */
	protected static URL getUrlForResource(String resource) {
		URL url = null;

		ClassLoader cl = ClassUtils.getClassLoader(ConfigurationFactory.class);
		if (cl != null) {
			url = cl.getResource(resource);
		}
		
		return url;
		
	}
	
	/**
	 * Get a list of {@link URL} for the resource
	 * @param resource
	 * @return l'URL 
	 */
	protected static List<URL> getUrlsForResource(String resource) {
		List<URL> urls = new ArrayList<URL>();
		ClassLoader cl = ClassUtils.getClassLoader(ConfigurationFactory.class);
		if (cl != null) {
			try {
				Enumeration<URL> urlEnmum = cl.getResources(resource);
				while(urlEnmum.hasMoreElements()) {
					urls.add(urlEnmum.nextElement());
				}
			}
			catch(IOException e) {
				//
			}
		}
		return urls;
	}


}
